use super::common::{MiddlewareRejectType, RequestStruct};
use super::middleware::call_middleware;
use std::collections::HashMap;

static mut ROUTES: Option<HashMap<String, Route>> = None;

#[allow(dead_code)]
pub(crate) fn check_init() -> bool {
    unsafe { ROUTES.is_some() }
}

#[allow(dead_code)]
pub(crate) fn add_route(route: Route) -> Result<(), RouterError> {
    unsafe {
        if let None = ROUTES {
            ROUTES = Some(HashMap::new());
        }
        let routes = ROUTES.as_mut().unwrap();
        if routes.contains_key(&route.key) {
            return Err(RouterError::KeyRepeatSet);
        } else {
            routes.insert(route.key.clone(), route);
            return Ok(());
        }
    }
}

#[allow(dead_code)]
pub(crate) fn del_route(key: &str) -> Result<Route, RouterError> {
    unsafe {
        if let Some(ref mut routes) = ROUTES {
            if let Some(route) = routes.remove(key) {
                return Ok(route);
            } else {
                return Err(RouterError::KeyNotExist);
            }
        } else {
            return Err(RouterError::RouteNotInit);
        }
    }
}

#[allow(dead_code)]
pub(crate) fn get_route(key: &str) -> Result<Route, RouterError> {
    unsafe {
        if let Some(ref mut routes) = ROUTES {
            if let Some(route) = routes.get(key) {
                return Ok(route.clone());
            } else {
                return Err(RouterError::KeyNotExist);
            }
        } else {
            return Err(RouterError::RouteNotInit);
        }
    }
}

#[allow(dead_code)]
pub(crate) fn call_route(key: &str, params: RequestStruct) -> RouterResult {
    let middleware_res = call_middleware(params);
    // Middleware blocks passage
    if !middleware_res.reject_passage {
        match middleware_res.reject_type {
            MiddlewareRejectType::Reject => {
                return RouterResult::MiddlewareBlocked;
            }
            MiddlewareRejectType::RejectAndClose => {
                return RouterResult::MiddlewareBlockedAndClosed;
            }
            _ => {}
        }
    }
    unsafe {
        if let Some(ref mut routes) = ROUTES {
            if let Some(route) = routes.get(key) {
                tokio::spawn(async move {
                    route.call(middleware_res.request);
                });
                return RouterResult::RouteExecuted;
            } else {
                return RouterResult::RouteMatchFail;
            }
        } else {
            return RouterResult::RouteNotInitialized;
        }
    }
}

#[derive(Clone, Debug)]
pub struct Route {
    pub key: String,
    pub handler: fn(RequestStruct),
}

impl Route {
    pub fn new(key: &str, handler: fn(RequestStruct)) -> Self {
        Route {
            key: key.to_string(),
            handler,
        }
    }
    pub fn call(&self, params: RequestStruct) {
        (self.handler)(params);
    }
}

/// Match route execution results
/// RouteExecuted: Route executed successfully
/// MiddlewareBlocked: Middleware blocked route execution
/// MiddlewareBlockedAndClosed: Middleware blocked route execution and closed the connection
#[derive(Debug, Clone)]
pub enum RouterResult {
    RouteExecuted,
    RouteMatchFail,
    RouteNotInitialized,
    MiddlewareBlocked,
    MiddlewareBlockedAndClosed,
}
impl std::fmt::Display for RouterResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RouterResult::RouteExecuted => write!(f, "Route executed successfully"),
            RouterResult::RouteMatchFail => write!(f, "Route match fail"),
            RouterResult::RouteNotInitialized => write!(f, "Route not initialized"),
            RouterResult::MiddlewareBlocked => write!(f, "Middleware blocked"),
            RouterResult::MiddlewareBlockedAndClosed => write!(f, "Middleware blocked and closed"),
        }
    }
}


#[derive(Debug, Clone)]
pub enum RouterError {
    RouteNotInit,
    KeyNotExist,
    KeyRepeatSet,
}

impl std::fmt::Display for RouterError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RouterError::RouteNotInit => write!(f, "The routing module has not been initialized"),
            RouterError::KeyNotExist => write!(f, "The route key does not exist"),
            RouterError::KeyRepeatSet => {
                write!(f, "The route key already exists, Cannot be set repeatedly")
            }
        }
    }
}